package com.yfunc.common.event.handlers;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import com.yfunc.common.event.Event;
import com.yfunc.common.event.EventException;
import com.yfunc.common.event.annotations.Handler;

@SuppressWarnings("rawtypes")
public class AnnotationEventHandler extends AbstractEventHandler {


	private Map<Class, Method> handlers;
	private Object handlerObject;

	public AnnotationEventHandler(Object object) {
		this.handlers = new HashMap<Class, Method>();
		this.handlerObject = object;
		addEventHandlerMethods(object);
	}

	private void addEventHandlerMethods(Object object) {

		if (!object.getClass().isAnnotationPresent(Handler.class)) {
			throw new EventException(
					"{0} Not Implements com.yfunc.event.annotations.Handler Annotation",
					this.handlerObject.getClass().getName());
		}

		for (Method method : object.getClass().getMethods()) {
			if (isEventHandleMethod(method)) {
				handlers.put(method.getParameterTypes()[0], method);
			}
		}
	}

	private boolean isEventHandleMethod(Method method) {
		if (!method.isAnnotationPresent(Handler.class)) {
			return false;
		}
		Class<?>[] types = method.getParameterTypes();
		if (types == null || types.length == 0) {
			return false;
		}
		return isInterface(types[0], Event.class);
	}

	private boolean isInterface(Class<?> clazz, Class<?> szInterface) {
		for (Class<?> interfac : clazz.getInterfaces()) {
			if (interfac.equals(szInterface)) {
				return true;
			}
		}
		if (clazz != null && clazz.getSuperclass() != null) {
			return isInterface(clazz.getSuperclass(), szInterface);
		}
		return false;
	}

	public Class<?>[] supportedEvents() {
		return handlers.keySet().toArray(new Class[0]);
	}

	@Override
	public void handle(Event event) {

		if (handlers.containsKey(event.getClass())) {
			Method method = handlers.get(event.getClass());
			try {
				method.invoke(this.handlerObject, event);
			} catch (Exception e) {
				throw new EventException("{0} Not Implements Method: {1}({2})",
						this.handlerObject.getClass().getName(),
						method.getName(), event.getClass().getName());
			}
		}
	}

}
